package com.yingxin.yxerp.module.easypoi.handler.impl;

import com.yingxin.yxerp.module.easypoi.handler.inter.IExcelVerifyHandler;
import org.apache.poi.ss.usermodel.Row;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 针对一组数据只要有一条数据验证不通过则整组数据回滚不通过
 *
 * @param <T>
 */
public abstract class ExcelImportRollbackVerifyHandler<T> implements IExcelVerifyHandler<T> {

    /**
     * 失败的Row
     */
    private final ThreadLocal<Map<String, List<Row>>> threadLocalFailRows = new ThreadLocal<>();

    /**
     * 成功的Row
     */
    private final ThreadLocal<Map<String, List<Row>>> threadLocalSuccessRows = new ThreadLocal<>();

    /**
     * 获取本地线程列表
     */
    public List<ThreadLocal> getThreadLocalList() {
        List threadLocalList = new ArrayList();
        threadLocalList.add(getThreadLocal());
        threadLocalList.add(getThreadLocalFlag());
        threadLocalList.add(getOtherThreadLocal());
        threadLocalList.add(threadLocalSuccessRows);
        threadLocalList.add(threadLocalFailRows);
        return threadLocalList;
    }

    /**
     * 获取本地其他数据存根
     * @return
     */
    public abstract ThreadLocal<Map<String, List>> getOtherThreadLocal();

    /**
     * 获取本地数据组得成功标记
     */
    public abstract ThreadLocal<Map<String, Boolean>> getThreadLocalFlag();

    /**
     * 修改验证失败集合
     */
    public abstract void modifyFailCollection(List<T> successCollection, List<T> failCollection, T obj);

    /**
     * 修改验证成功集合
     */
    public abstract void modifySuccessCollection(List<T> successCollection, List<T> failCollection, T obj);

    /**
     * 获取数据组Key
     */
    public abstract String getKey(T obj);

    /**
     * 本地存根：添加成功Row
     */
    public void addSuccessRow(Row row, T obj) {
        String key = getKey(obj);
        // 初始化及获取RowsMap
        Map<String, List<Row>> successMap = initSuccessOrFailMap(threadLocalSuccessRows);
        Map<String, List<Row>> failMap = initSuccessOrFailMap(threadLocalFailRows);
        // 处理数据
        Map<String, Boolean> threadLocalFlag = getThreadLocalFlag().get();
        if (!threadLocalFlag.get(key)) {
            if (null != successMap.get(key)) {
                moveSuccessMapToFailMap(key, successMap, failMap);
            } else {
                addRow(row, key, failMap);
            }
        } else {
            addRow(row, key, successMap);
        }
    }

    /**
     * 本地存根：添加失败Row
     */
    public void addFailRow(Row row, T obj) {
        String key = getKey(obj);
        // 初始化及获取RowsMap
        Map<String, List<Row>> successMap = initSuccessOrFailMap(threadLocalSuccessRows);
        Map<String, List<Row>> failMap = initSuccessOrFailMap(threadLocalFailRows);
        // 处理数据
        if (null != successMap.get(key)) {
            moveSuccessMapToFailMap(key, successMap, failMap);
        }
        addRow(row, key, failMap);
    }

    /**
     * 获取失败得Row集合
     */
    public List<Row> getFailRows() {
        List<Row> result = getRows(threadLocalFailRows.get());
        return result;
    }

    /**
     * 获取成功得Row集合
     */
    public List<Row> getSuccessRows() {
        List<Row> result = getRows(threadLocalSuccessRows.get());
        return result;
    }

    private Map<String, List<Row>> initSuccessOrFailMap(ThreadLocal<Map<String, List<Row>>> threadLocalSuccessRows) {
        Map<String, List<Row>> rowMap = threadLocalSuccessRows.get();
        if (null == rowMap) {
            rowMap = new HashMap<>();
            threadLocalSuccessRows.set(rowMap);
        }
        return rowMap;
    }

    private void moveSuccessMapToFailMap(String key, Map<String, List<Row>> successMap, Map<String, List<Row>> failMap) {
        if (null != successMap.get(key)) {
            List<Row> rowList = successMap.get(key);
            successMap.remove(key);
            if (null == failMap.get(key)) {
                List<Row> failRowList = new ArrayList<>();
                failRowList.addAll(rowList);
                failMap.put(key, failRowList);
            } else {
                failMap.get(key).addAll(rowList);
            }
        }
    }

    private void addRow(Row row, String key, Map<String, List<Row>> rowMap) {
        if (null == rowMap.get(key)) {
            List<Row> rowList = new ArrayList<>();
            rowList.add(row);
            rowMap.put(key, rowList);
        } else {
            rowMap.get(key).add(row);
        }
    }

    private List<Row> getRows(Map<String, List<Row>> map) {
        List<Row> result = new ArrayList<>();
        if (null != map) {
            for (List<Row> item : map.values()) {
                result.addAll(item);
            }
        }
        return result;
    }
}
